home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Inside Mac Games Volume 4 #1 & #2
/
IMG 34 JanFeb 1996.iso
/
Essentials
/
MGD4Codeƒ
/
GoodbyWorld1.c
< prev
next >
Wrap
Text File
|
1995-12-16
|
19KB
|
453 lines
//==============================================================================================\\
// ----------------------------------------------------------------------------------- \\
// GoodbyWorld1.c version 1.0.0 copyright © 1993…1995 Jamie McCornack \\
// ----------------------------------------------------------------------------------- \\
// Demo program for Macintosh GameWriter 1.0.0, a training program… \\
// …for beginning Mac game programmers. MGW1 includes MGWExterns1.h, MGWUtilities1.c,… \\
// …MGWSound1.c, MGWGraphics1.c, MGWGraphicsBWLite1.c, HelloWorld.rsrc and an assortment… \\
// …of demo programs; projects HelloWorld1.π etc. and source code files HelloWorld1.c etc. \\
// A tutorial is available in Tricks of the Mac Game Programming Gurus, published… \\
// …by Hayden Books, August 1995. \\
// \\
// This code is offered by the copyright holders for no fee and for whatever use… \\
// …you care to make of it, but we do hope you remember where it came from. \\
// \\
// Please send bug reports to MacGameDev at America OnLine. macgamedev@aol.com \\
// Suggestions and observations are also appreciated. \\
// Updates and upgrades will be available now and then from the above e-mail address. \\
//==============================================================================================\\
// This program opens a window, displays an 8-bit color background, and at first…
// …mouseclick, runs a clam across the screen. At second mouseclick, the clam stops,…
// …talks, and flaps its face for 40 frames. Then it blows up, and the program quits.
#include "MGWExterns1.h"
#define kPutInFront (WindowPtr)-1L
#define kWaitTicks 4L // Sets the delay in Ticks between frames. Try 3. Try 2. Try 0.
#define kStepLength 14 // Sets the distance in pixels between sprite moves.
#define kFrontFace 0
#define kBlinkFace 1
#define kEehFace 2
#define kOohFace 3
#define kStepRightFace 4
#define kWalkRightFace 5
#define kRunRightFace 6
#define kMaxClamFaces 7
#define kBoomFace0 0 // Couldn't resist the onomonopea.
#define kBoomFace1 1
#define kBoomFace2 2
#define kBoomFace3 3
#define kBoomFace4 4
#define kBoomFace5 5
#define kBoomFace6 6
#define kBoomFace7 7
#define kMaxFireFaces 8
#define kShrinkFactor -16 // -kShrinkFactor expands the explosion rect from 32x32.
// For optimum size (64x64), kShrinkFactor = -16. Try -48. Try 0.
// But remember: any scaling slows down CopyBits.
// The resource constants--with 'r' prefix like Apple wants them.}
#define rClamFacesID 136 // The 'PICT' ID# where the views of the clam are located.}
#define rClamMasksID 130 // The 'PICT' ID# where the clam masks are located.}
#define rFireFacesID 141 // 141 for cartoon version, 142 for photo version.}
#define rFireMasksID 140 // The 'PICT' ID# where the explosion masks are located.}
#define rBackgroundID 134 // The 'PICT' ID# where the background picture is located.}
#define rMainWindowID 128 // The 'WIND' resource ID# for the main window.}
#define rGoodbyeSndID 3000
#define rFootstepSndID 3001
#define rImpactSndID 3002
#define rDizzySndID 3003
#define rExplodeSndID 3004
#define kColorBitsPreferred 8
typedef struct
{
Rect face;
Rect mask;
} tSpriteType;
Rect bigPictureRect, fireFacesRect, fireMasksRect,fireIsAtRect,
clamFacesRect, clamMasksRect, clamIsAtRect, clamWasAtRect, clamComboRect;
CGrafPtr workCPort, clamFacesCPort, fireFacesCPort, backgroundCPort;
GrafPtr mainWindow, clamMasksPort, fireMasksPort;
Boolean itWorked;
long targetTick;
short thisSprite, thisFaceCounter;
tSpriteType clamSprite[kMaxClamFaces], fireSprite[kMaxFireFaces];
extern Boolean gUserWantsSound;
//============================================================== Prototypes
void InitAll(void);
void OpenMainWindow (void);
void SetTheRects(void);
void SetTheCPorts(void);
void ShowClam (void);
void DoDelay (void);
void RunRight (void);
void LipSynch (void);
void ShowExplosion (void);
void Explode (void);
void SayGoodbye (void);
//============================================================== Functions
//-------------------------------------------------------------- InitAll
void InitAll(void)
{
InitToolbox();
if (WhatsOurDepth() < kColorBitsPreferred) // Compare color depth with what we want.
YellowAlert(kPref8BitColor); // If smaller, notify user.
if (WhatsOurDepth() > kColorBitsPreferred) // Compare color depth with what we want.
YellowAlert(kPrefDownTo8BitColor); // If larger, notify user.
gUserWantsSound = TRUE;
InitializeForSound();
SetTheRects(); // Since some of these Rects define fields in CGrafPorts,…
SetTheCPorts(); // …set the rects before setting the ports.
thisSprite = kFrontFace;
thisFaceCounter = 0;
targetTick = TickCount() + kWaitTicks;
HideCursor(); // This demo doesn't use mouse input, so why look at it?
}
//-------------------------------------------------------------- OpenMainWindow
void OpenMainWindow (void)
{
mainWindow = GetNewCWindow(128, 0L, kPutInFront); // Load window from resource.
ShowWindow((GrafPtr)mainWindow); // Now display it.
SetPort((GrafPtr)mainWindow); // Make its port current.
ClipRect(&bigPictureRect); // Set its clip region.
CopyRgn(mainWindow->clipRgn, mainWindow->visRgn); // Set its visRgn.
ForeColor(blackColor); // Set its pen color to black.
BackColor(whiteColor); // Set background color white.
}
//-------------------------------------------------------------- SetTheRects
void SetTheRects(void) // The most tedious part of programming this type of game.
{
SetRect(&clamFacesRect, 0, 0, 448, 64); // Size and shape of PixMap for the clam faces.
SetRect(&clamMasksRect, 0, 0, 448, 32); // Size and shape of BitMap for the clam masks.
SetRect(&bigPictureRect, 0, 0, 512, 322); // The shape of the picture we'll put in the main window, workCPort and backgroundCPort.}
SetRect(&clamIsAtRect, 200, 244, 232, 276); // The shape (32 x 32) of the images of Clem, and the position of the first image.}
SetRect(&fireFacesRect, 0, 0, 192, 192); // Size and shape of PixMap for the fire faces.
SetRect(&fireMasksRect, 0, 0, 192, 192); // Size and shape of PixMap for the fire masks.
clamWasAtRect = clamIsAtRect; // Initializing clamIsAtRect...it has to start somewhere, and this is handy.}
clamComboRect = clamIsAtRect;
// And now, the tedium. In this sample, all we're doing is showing the clam running across the screen to the right.}
// However, if you use ResEdit and look at 'PICT' 129 in Sample.rsrc, you'll find 28 different views of the clam.}
// If we wanted the clam to run left too, and walk slowly, and face the user, and blink its eyes, we'd be calling…}
// …SetRect 56 times--one face and one mask per sprite. And if we had jumping clams and rear views of clams…}
// …and starfish and clamdiggers and other hazards of the clam environment, we might have hundreds of rects to set.}
SetRect(&clamSprite[kFrontFace].face, 320, 32, 352, 64); // The shape and position of sprite[kFrontFace].face on clamFacesCPort.portPixMap.
SetRect(&clamSprite[kFrontFace].mask, 320, 0, 352, 32); // The shape and position of sprite[kFrontFace].mask on masksPort.portPixMap.
SetRect(&clamSprite[kBlinkFace].face, 320, 0, 352, 32); // Note that some faces (e.g. eyes open or closed) use the same mask,…
SetRect(&clamSprite[kBlinkFace].mask, 320, 0, 352, 32); // …since they have the same silhouette.}
SetRect(&clamSprite[kEehFace].face, 352, 0, 384, 32); // I could write more comments here, but setting these rects…
SetRect(&clamSprite[kEehFace].mask, 352, 0, 384, 32); // …is already tedious enough without a bunch of busy-work.
SetRect(&clamSprite[kOohFace].face, 352, 32, 384, 64);
SetRect(&clamSprite[kOohFace].mask, 352, 0, 384, 32);
SetRect(&clamSprite[kStepRightFace].face, 192, 0, 224, 32);
SetRect(&clamSprite[kStepRightFace].mask, 192, 0, 224, 32);
SetRect(&clamSprite[kWalkRightFace].face, 224, 0, 256, 32);
SetRect(&clamSprite[kWalkRightFace].mask, 224, 0, 256, 32);
SetRect(&clamSprite[kRunRightFace].face, 160, 0, 192, 32); // BTW, there are plenty more clam faces and masks in the 'PICT's,…
SetRect(&clamSprite[kRunRightFace].mask, 160, 0, 192, 32); // …if you feel you need rect setting practice. :-)
SetRect(&fireSprite[kBoomFace0].face, 0, 0, 64, 64);
SetRect(&fireSprite[kBoomFace0].mask, 0, 0, 64, 64);
SetRect(&fireSprite[kBoomFace1].face, 64, 0, 128, 64);
SetRect(&fireSprite[kBoomFace1].mask, 64, 0, 128, 64);
SetRect(&fireSprite[kBoomFace2].face, 128, 0, 192, 64);
SetRect(&fireSprite[kBoomFace2].mask, 128, 0, 192, 64);
SetRect(&fireSprite[kBoomFace3].face, 0, 64, 64, 128);
SetRect(&fireSprite[kBoomFace3].mask, 0, 64, 64, 128);
SetRect(&fireSprite[kBoomFace4].face, 64, 64, 128, 128);
SetRect(&fireSprite[kBoomFace4].mask, 64, 64, 128, 128);
SetRect(&fireSprite[kBoomFace5].face, 128, 64, 192, 128);
SetRect(&fireSprite[kBoomFace5].mask, 128, 64, 192, 128);
SetRect(&fireSprite[kBoomFace6].face, 0, 128, 64, 192);
SetRect(&fireSprite[kBoomFace6].mask, 0, 128, 64, 192);
SetRect(&fireSprite[kBoomFace7].face, 64, 128, 128, 192);
SetRect(&fireSprite[kBoomFace7].mask, 64, 128, 128, 192);
}
//-------------------------------------------------------------- SetTheCPorts
void SetTheCPorts(void) // Create the CGrafPorts and load their .portPixMap fields.
{
// Create BitMap for clam masks. NOTE THIS IS A BITMAP!
CreateOffScreenBitMap (&clamMasksRect, &clamMasksPort);
LoadGraphic (rClamMasksID); // …load 'PICT' resource for the clam masks.
// Create BitMap for fire masks. NOTE THIS IS A BITMAP!
CreateOffScreenBitMap (&fireMasksRect, &fireMasksPort);
LoadGraphic (rFireMasksID); // …load 'PICT' resource for the fire masks.
// Create PixMap for clam faces.
CreateOffScreenPixMap (&clamFacesRect, &clamFacesCPort);
LoadGraphic (rClamFacesID); // …load 'PICT' resource for the clam faces.
// Create PixMap for fire faces.
CreateOffScreenPixMap (&fireFacesRect, &fireFacesCPort);
LoadGraphic (rFireFacesID); // …load 'PICT' resource for the fire faces.
// Create PixMap for the background.
CreateOffScreenPixMap (&bigPictureRect, &backgroundCPort);
LoadGraphic(rBackgroundID); // …load 'PICT' resource for the background picture.
// Create PixMap for offscreen graphics work.
CreateOffScreenPixMap (&bigPictureRect, &workCPort);
OpenMainWindow();
//{This fills the main window with the background picture, so the user can see it.
CopyBits(&((GrafPtr)backgroundCPort)->portBits,
&((GrafPtr)mainWindow)->portBits,
&bigPictureRect, &bigPictureRect, srcCopy, mainWindow->visRgn);
// This fills the workCPort.portPixMap with the background picture, so updates can be done quickly.
CopyBits(&((GrafPtr)backgroundCPort)->portBits,
&((GrafPtr)workCPort)->portBits,
&bigPictureRect, &bigPictureRect, srcCopy, mainWindow->visRgn);
}
//-------------------------------------------------------------- ShowClam
void ShowClam (void) // Do the animation and make it appear on the screen.
{
CopyMask(&((GrafPtr)clamFacesCPort)->portBits,
&((GrafPtr)clamMasksPort)->portBits,
&((GrafPtr)workCPort)->portBits,
&clamSprite[thisSprite].face,
&clamSprite[thisSprite].mask,
&clamIsAtRect);
// Now there is an image of a clam in the new position in workMap. If we had done this work in…
// mainWindow, we would have seen considerable flickering. Instead, we did it offscreen, and left the…
// previous image of the clam visible on the screen while we worked.
UnionRect(&clamWasAtRect, &clamIsAtRect, &clamComboRect);
// Find the smallest rectangle which will cover the old position of the clam and the new.
CopyBits(&((GrafPtr)workCPort)->portBits,
&(((GrafPtr)mainWindow)->portBits),
&clamComboRect, &clamComboRect, srcCopy, mainWindow->visRgn);
// Copy the contents of comboRect from workCPort->portPixMap to the main window. In one swell foop, the old clam…}
//…will be erased, and the new clam overlayed onto the background picture. Wallah! Flicker-free animation!}
CopyBits(&((GrafPtr)backgroundCPort)->portBits,
&(((GrafPtr)workCPort)->portBits),
&clamIsAtRect, &clamIsAtRect, srcCopy, mainWindow->visRgn);
// Restore the workCPort by covering up our clam with the background it obscures.
// This way, workCPort->portPixMap is identical to backgroundCPort->portPixMap,…
// without having to copy the entire PixMap.
}
//-------------------------------------------------------------- ShowExplosion
void ShowExplosion (void) // Much like ShowClams().
{
CopyMask(&((GrafPtr)fireFacesCPort)->portBits,
&((GrafPtr)fireMasksPort)->portBits,
&((GrafPtr)workCPort)->portBits,
&fireSprite[thisSprite].face,
&fireSprite[thisSprite].mask,
&fireIsAtRect);
// Don't need UnionRect() here because fireIsAt rect doesn't move.
CopyBits(&((GrafPtr)workCPort)->portBits,
&(((GrafPtr)mainWindow)->portBits),
&fireIsAtRect, &fireIsAtRect, srcCopy, mainWindow->visRgn);
CopyBits(&((GrafPtr)backgroundCPort)->portBits,
&(((GrafPtr)workCPort)->portBits),
&fireIsAtRect, &fireIsAtRect, srcCopy, mainWindow->visRgn);
}
//-------------------------------------------------------------- DoDelay
// This is the companion function to the above function (LogNextTick()).
// We do nothing but loop until TickCount() catches up with (or passes) our…
// global variable tickNext.
void DoDelay (void)
{
do
{
}
while (TickCount() < targetTick); // Loop until TickCount() catches up.
targetTick = TickCount() + kWaitTicks;
}
//-------------------------------------------------------------- RunRight
void RunRight (void) //Gives a sequence of views of the clam running to the right.
{
switch (thisSprite) //If the current view of the clam is…
{ case (kStepRightFace): // …kStepRightFace, then set thisSprite to…
{ thisSprite = kWalkRightFace; // …kWalkRightFace, and if it is currently…
break; }
case (kWalkRightFace): // …kWalkFace, then set it to…
{ thisSprite = kRunRightFace; // …kRunRightFace.
PlayASound(rFootstepSndID, kHighestSoundPriority); // Note sound priority.
break; }
// And if it was neither kStepRightFace nor kWalkRightFace, then thisSprite was either…
default : // …kRunRightFace, or what it was when RunRight()…
thisSprite = kStepRightFace; // …was called, so we set it to kStepRightFace
}
clamWasAtRect = clamIsAtRect; // Store the clam's current position as its last position,…
// …we'll be erasing it next time through the loop.
OffsetRect(&clamIsAtRect, kStepLength, 0); // Set the clam's next position--it'll be…
// …kStepLength pixels to the right of its last position.
if (clamIsAtRect.left > 512) // If the clam has wandered out of sight,…
{ // …set the right border of clamIsAtRect…
clamIsAtRect.right = 0; // …to the left edge of the screen…
clamIsAtRect.left = -32; // …and move the left border of clamIsAtRect…
} // …as needed to maintain its 32 x 32 shape & size.
ShowClam(); // The actual animation routine!
DoDelay(); // Do nothing for a while. In a real game, you'll want to use time more wisely.
}
//-------------------------------------------------------------- LipSynch
// This routine has the clam stop moving, face the screen, and move its face.
// Note there are diferences in sequence between this routine for "Goodbye, World,"…
// …and the "Hello, World," versions, since the different words require different moves.
void LipSynch (void)
{
while (thisFaceCounter < 40)
{
thisFaceCounter = thisFaceCounter + 1;
switch (thisFaceCounter)
{ case (1):
{ thisSprite = kFrontFace;
break; }
case (2):
{ thisSprite = kEehFace;
break; }
case (4):
{ thisSprite = kFrontFace;
break; }
case (5):
{ thisSprite = kOohFace;
break; }
case (9):
{ thisSprite = kEehFace;
break; }
case (11):
{ thisSprite = kFrontFace;
break; }
case (12):
{ thisSprite = kBlinkFace;
break; }
case (14):
{ thisSprite = kOohFace;
break; }
case (17):
{ thisSprite = kFrontFace;
break; }
case (35):
{ thisSprite = kBlinkFace;
break; }
case (38):
{ thisSprite = kFrontFace;
break; }
}
ShowClam();
DoDelay(); // Do nothing for a while, so the lips don't flap too fast.
}
}
//-------------------------------------------------------------- Explode
// This routine sequences the explosion images.
void Explode (void)
{
while (thisFaceCounter < 40)
{
thisFaceCounter++; // Might as well make it look like a C program. Increments thisFaceCounter.
switch (thisFaceCounter)
{
case (0):
{ thisSprite = kBoomFace0;
break; }
case (1):
{ thisSprite = kBoomFace1;
break; }
case (2):
{ thisSprite = kBoomFace2;
break; }
case (3):
{ thisSprite = kBoomFace3;
break; }
case (4):
{ thisSprite = kBoomFace4;
break; }
case (5):
{ thisSprite = kBoomFace5;
break; }
case (6):
{ thisSprite = kBoomFace6;
break; }
case (7):
{ thisSprite = kBoomFace7; // kBoomFace7 is blank, BTW.
break; }
}
ShowExplosion();
DoDelay(); // Do nothing for a while, so the frame rate isn't too high.
}
}
//-------------------------------------------------------------- SayGoodbye
// This routine sequences the explosion images.
void SayGoodbye (void)
{
PlayASound(rGoodbyeSndID, kMediumSoundPriority);
LipSynch();
thisFaceCounter = -1; // Explode() will increment this to zero in its first loop.
fireIsAtRect = clamIsAtRect; // Locates the explosion at the clam's final position.
InsetRect(&fireIsAtRect,kShrinkFactor,kShrinkFactor); // Negative numbers "outset" rect.
PlayASound(rExplodeSndID, kMediumSoundPriority);
Explode();
}
//-------------------------------------------------------------- main
//----------------------------------------------------------------------
void main(void)
{
InitAll();
while (!Button()) // Before the user presses the mouse button, nothing happens.
{
}
while (Button()) // When the user presses the mouse button, nothing happens.
{
}
while (!Button()) // When the mouse button is released, continue to…
{
RunRight(); // …show the clam running right (duh),…
}
while (Button()) // …until the button is pushed.
{ // Do nothing until the button is released.
}
SayGoodbye();
CloseDownSound();
} // And we're done.
//------------------------------------------------------------------------------------------\\
// End HelloWorld1.c \\
//------------------------------------------------------------------------------------------\\